package org.kaindorf.kainflix.metadata.service.impl;

import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.kaindorf.kainflix.metadata.exceptions.ResourceNotFoundException;
import org.kaindorf.kainflix.metadata.exceptions.IllegalDataGivenException;
import org.kaindorf.kainflix.metadata.repository.MetaDataRepository;
import org.kaindorf.kainflix.metadata.repository.entity.Metadata;
import org.kaindorf.kainflix.metadata.service.MetaDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.UnexpectedRollbackException;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MetaDataServiceImpl implements MetaDataService {

    private static final Logger logger = LogManager.getLogger(MetaDataServiceImpl.class);
    
    @Autowired
    private MetaDataRepository metaDataRepository;

    @Override
    @Transactional
    public Metadata saveMetaData(Metadata metaData) {
        try {
            Metadata savedMetadata = metaDataRepository.save(metaData);
            logger.info("saved new metadata (" + savedMetadata.getId() + ") successfully");
            return savedMetadata;
        } catch (DataIntegrityViolationException ex) {
            throw new IllegalDataGivenException("could not save metadata - the given metadata productId + language might already exist or some other parameters are not valid", ex);
        } catch (UnexpectedRollbackException | DataAccessException ex) { // QUESTION: what is the best exception to catch?
            throw new IllegalDataGivenException("could not save metadata", ex);
        }
    }

    @Override
    @Transactional(readOnly = true)
    public Metadata getMetaDataById(long metaDataId) {
        Metadata metaData = metaDataRepository.findOne(metaDataId);
        if (metaData == null) {
            throw new ResourceNotFoundException("the meta data with the id " + metaDataId + " could not be found");
        }
        return metaData;
    }

    @Override
    @Transactional
    public Metadata updateMetaData(Metadata metaData) {
        Metadata existingMetaData = metaDataRepository.findOne(metaData.getId());
        existingMetaData.setIndentifier(metaData.getIndentifier());
        existingMetaData.setProductId(metaData.getProductId());
        existingMetaData.setValue(metaData.getValue());
        // QUESTION: the real SQL will be executed at the end of the transaction (after the logging), 
        // and as it is unsure that the update will be successful, 
        // what message exactly should be logged?
        logger.info("updating metadata (" + existingMetaData.getId() + ")");
        return existingMetaData;
    }

    @Override
    @Transactional(readOnly = true)
    public Iterable<Metadata> getAllMetaData() {
        return metaDataRepository.findAll();
    }

    @Override
    @Transactional
    public void delete(long metaDataId) {
        if (metaDataRepository.exists(metaDataId)) {
            metaDataRepository.delete(metaDataId);
            logger.info("deleting metadata (" + metaDataId + ")");
        } else {
            // QUESTION: soll eine exception geworfen werden oder einfach ignoriert werden?
            throw new ResourceNotFoundException("meta data with the ID " + metaDataId + " does not exist");
        }
    }

    @Transactional(readOnly = true)
    @Override
    public List<Metadata> getAllMetaDataWithProductId(Long productId) {
        return metaDataRepository.findByProductId(productId);
    }

}
